/**
 * Node2DAnimator.java Created on : 21 mai 07
 */
package fr.limsi.seven;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;

/**
 * @author Camille
 */
public class Node2DAnimator {
	/**
	 * Animation callback delay in milliseconds
	 */
	public static final int ANIMATION_DELAY = 15;

	/**
	 * Animation length in image count
	 */
	public static final int ANIMATION_LENGTH = 180;

	/**
	 * The associated <i>Node2D</i>
	 */
	protected Node2D node2D;

	/**
	 * Timer for the currentAnimation
	 */
	protected Timer animationTimer;

	/**
	 * Constructs a {@link Node2D} animator
	 * 
	 * @param node2D
	 *            the {@link Node2D} to animate
	 */
	public Node2DAnimator(Node2D node2D) {
		this.node2D = node2D;
	}

	/**
	 * Creates the {@link Animation} for the {@link Node2D} {@link StarDrawer} opening
	 * 
	 * @return the created {@link Animation}
	 */
	public Animation getShowStarAnimation() {

		Thread anim = new Thread() {
			@Override
			public void run() {

				showStar();

				waitForAnimationTimer();

				node2D.getPyramide2D().getAnimationMonitor().endReachedBy(this);
			}
		};

		anim.setName("Show star");

		return new Animation(anim);
	}

	/**
	 * Creates the {@link Animation} for the {@link Node2D} {@link StarDrawer} closing
	 * 
	 * @return the created {@link Animation}
	 */
	public Animation getCloseStarAnimation() {

		Thread anim = new Thread() {
			@Override
			public void run() {

				closeStar();

				waitForAnimationTimer();

				node2D.getPyramide2D().getAnimationMonitor().endReachedBy(this);
			}
		};

		anim.setName("Close star");

		return new Animation(anim);
	}

	/**
	 * Launches the node {@link StarDrawer} opening animation
	 */
	public void showStar() {

		stopAnimationTimer();

		if (!node2D.getStarDrawer().isShowing()) {

			node2D.getStarDrawer().setVisible(true);
			node2D.getStarDrawer().setBounds(node2D.getX(), node2D.getY(), 0, 0);
			node2D.getPyramide2D().add(node2D.getStarDrawer(), 0);
			node2D.getStarDrawer().setParentContainer(node2D.getPyramide2D());

			float dWidth = node2D.getStarDrawer().getMaxWidth() / (ANIMATION_LENGTH / ANIMATION_DELAY);
			float dHeight = node2D.getStarDrawer().getMaxHeight() / (ANIMATION_LENGTH / ANIMATION_DELAY);

			// Width step correction
			if (dWidth < 0 && dWidth > -1) {
				dWidth = -1;
			}

			if (dWidth > 0 && dWidth < 1) {
				dWidth = 1;
			}

			// Height step correction
			if (dHeight < 0 && dHeight > -1) {
				dHeight = -1;
			}

			if (dHeight > 0 && dHeight < 1) {
				dHeight = 1;
			}

			final float deltaWidth = dWidth;
			final float deltaHeight = dHeight;

			ActionListener appearCallback = new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					maximizeInfo(deltaWidth, deltaHeight);
				}
			};

			animationTimer = new Timer(0, appearCallback);
			animationTimer.setDelay(ANIMATION_DELAY);
			animationTimer.start();
		}
	}

	/**
	 * Launches the node {@link StarDrawer} closing animation
	 */
	public void closeStar() {

		stopAnimationTimer();

		float dWidth = node2D.getStarDrawer().getWidth() / (ANIMATION_LENGTH / ANIMATION_DELAY);
		float dHeight = node2D.getStarDrawer().getHeight() / (ANIMATION_LENGTH / ANIMATION_DELAY);

		// Width step correction
		if (dWidth < 0 && dWidth > -1) {
			dWidth = -1;
		}

		if (dWidth > 0 && dWidth < 1) {
			dWidth = 1;
		}

		// Height step correction
		if (dHeight < 0 && dHeight > -1) {
			dHeight = -1;
		}

		if (dHeight > 0 && dHeight < 1) {
			dHeight = 1;
		}

		final float deltaWidth = dWidth;
		final float deltaHeight = dHeight;
		final int startX = node2D.getStarDrawer().getX() + (node2D.getStarDrawer().getWidth() / 2);
		final int startY = node2D.getStarDrawer().getY() + (node2D.getStarDrawer().getHeight() / 2);

		ActionListener disappearCallback = new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				minimizeInfo(deltaWidth, deltaHeight, startX, startY);
			}
		};

		animationTimer = new Timer(0, disappearCallback);
		animationTimer.setDelay(ANIMATION_DELAY);
		animationTimer.start();
	}

	/**
	 * Callback function for <i>showStar()</i>
	 * 
	 * @param dWidth
	 *            interpolation step for the info width
	 * @param dHeight
	 *            interpolation step for the info height
	 */
	protected void maximizeInfo(float dWidth, float dHeight) {

		// Interpolation
		int width = (int) (node2D.getStarDrawer().getWidth() + dWidth);
		int height = (int) (node2D.getStarDrawer().getHeight() + dHeight);

		if (width >= node2D.getStarDrawer().getMaxWidth() || height >= node2D.getStarDrawer().getMaxHeight()) {
			stopAnimationTimer();
			node2D.getPyramide2D().repaint();
		}
		else {
			node2D.getStarDrawer().setBounds(node2D.getX() - (width / 2), node2D.getY() - (height / 2), width, height);
		}
	}

	/**
	 * Callback function for <i>closeStar()</i>
	 * 
	 * @param dWidth
	 *            interpolation step for the info width
	 * @param dHeight
	 *            interpolation step for the info height
	 * @param startX
	 *            the {@link Node2D} {@link StarDrawer} middle x coordinate on call
	 * @param startY
	 *            the {@link Node2D} {@link StarDrawer} middle y coordinate on call
	 */
	protected void minimizeInfo(float dWidth, float dHeight, int startX, int startY) {

		// Interpolation
		int width = (int) (node2D.getStarDrawer().getWidth() - dWidth);
		int height = (int) (node2D.getStarDrawer().getHeight() - dHeight);

		if (width <= 0 || height <= 0) {
			stopAnimationTimer();
			node2D.getPyramide2D().remove(node2D.getStarDrawer());
			node2D.getPyramide2D().repaint();
		}
		else {
			node2D.getStarDrawer().setBounds(startX - (width / 2), startY - (height / 2), width, height);
		}
	}

	/**
	 * Stops the <i>animationTimer</i>
	 */
	public synchronized void stopAnimationTimer() {
		if (animationTimer != null) {
			animationTimer.stop();
		}

		notify();
	}

	/**
	 * Waits for the <i>animationTimer</i> to stop
	 */
	public synchronized void waitForAnimationTimer() {
		while (animationTimer != null && animationTimer.isRunning()) {
			try {
				wait();
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
